package com.linxi.rpccompensation.aop;

import com.linxi.rpccompensation.annotation.Compensation;
import com.linxi.rpccompensation.service.PushLogService;
import com.linxi.rpccompensation.util.HttpUtil;
import com.linxi.rpccompensation.util.JsonUtils;
import com.linxi.rpccompensation.util.SpringUtil;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * @description: 补偿机制切面
 * @author: linxi
 * @date: 2021/9/22
 */
@Aspect
@Configuration
public class CompensationJoinPoint {

	private Logger logger = LoggerFactory.getLogger(CompensationJoinPoint.class);

	@Resource
	private PushLogService pushLogService;

	@Pointcut("@annotation(com.linxi.rpccompensation.annotation.Compensation)")
	public void aopPoint() {
	}

	/**
	 * 代理方法必须为布尔值返回类型或无返回值类型
	 *
	 * @param jp
	 * @param compensation
	 * @return
	 * @throws Throwable
	 */
	@Around("aopPoint()&& @annotation(compensation)")
	public Object doPush(ProceedingJoinPoint jp, Compensation compensation) throws Throwable {
		// TODO:业务拆分实现注解，去除无用方法
		String parmToJson = "";
		String id = "";
		String apiStr = "";
		if (1 == jp.getArgs().length) {
			parmToJson = getOneParamToJson(jp);
			apiStr = compensation.api();
			HttpUtil httpUtil = SpringUtil.getBean(HttpUtil.class);
			apiStr = httpUtil.returnApiUrl(apiStr);
		} else if (2 == jp.getArgs().length) {
			Map<String, String> map = getParamToMap(jp);
			apiStr = map.get("api");
			parmToJson = map.get("pushLog");
		}
		id = pushLogService.savePushLog(apiStr, parmToJson);

		logger.info("执行方法 method  ：{} ", getMethod(jp).getName());
		logger.info("执行参数 api     ：{} ", apiStr);
		logger.info("执行参数 args    ：{} ", parmToJson);

		// 返回结果
		try {
			Object proceed = jp.proceed();
			if (HttpUtil.doSure(String.valueOf(proceed))) {
				pushLogService.updateSuccess(id);
			} else {
				pushLogService.updateFail(id);
			}
			return proceed;
		} catch (Exception e) {
			pushLogService.updateFail(id);
			logger.info("异常信息 exception ：{} ", e.getMessage());
			return false;
		}
	}

	/**
	 * 拿到添加注解的方法名
	 *
	 * @param jp
	 * @return
	 * @throws NoSuchMethodException
	 */
	private Method getMethod(JoinPoint jp) throws NoSuchMethodException {
		Signature sig = jp.getSignature();
		MethodSignature methodSignature = (MethodSignature) sig;
		return jp.getTarget().getClass().getMethod(methodSignature.getName(), methodSignature.getParameterTypes());
	}

	/**
	 * 从入参对象拿到指定的属性对应值
	 *
	 * @param attr
	 * @param args
	 * @return
	 */
	public String getAttrValue(String attr, Object[] args) {
		String filedValue = null;
		for (Object arg : args) {
			try {
				if (StringUtils.isNotBlank(filedValue)) {
					break;
				}
				filedValue = BeanUtils.getProperty(arg, attr);
			} catch (Exception e) {
				logger.error("获取属性值失败 attr：{}", attr, e);
			}
		}
		return filedValue;
	}


	/**
	 * 从代理方法获取参数名和值，并且封装成json字符串
	 *
	 * @param joinPoint
	 * @return
	 */
	public String getParamToJson(ProceedingJoinPoint joinPoint) {
		Signature signature = joinPoint.getSignature();
		MethodSignature methodSignature = (MethodSignature) signature;
		Object[] args = joinPoint.getArgs();
		String[] parameterNames = methodSignature.getParameterNames();
		Map map = new HashMap(parameterNames.length);
		for (int i = 0; i < parameterNames.length; i++) {
			map.put(parameterNames[i], args[i]);
		}
		return JsonUtils.toJSONString(map);
	}

	/**
	 * 从代理方法获取参数名和值，并且封装成Map
	 *
	 * @param joinPoint
	 * @return
	 */
	public Map<String, String> getParamToMap(ProceedingJoinPoint joinPoint) {
		Signature signature = joinPoint.getSignature();
		MethodSignature methodSignature = (MethodSignature) signature;
		Object[] args = joinPoint.getArgs();
		String[] parameterNames = methodSignature.getParameterNames();
		Map<String, String> map = new HashMap(parameterNames.length);
		for (int i = 0; i < parameterNames.length; i++) {
			map.put(parameterNames[i], String.valueOf(args[i]));
		}
		return map;
	}

	/**
	 * 只获取单一参数
	 *
	 * @param joinPoint
	 * @return
	 */
	public String getOneParamToJson(ProceedingJoinPoint joinPoint) {
		Object[] args = joinPoint.getArgs();
		return JsonUtils.toJSONString(args[0]);
	}
}
